// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2018 Spreadtrum Communications Inc.
 * Copyright (C) 2018 Linaro Ltd.
 */
 #include <common.h>
#include <config.h>
#include <asm/io.h>
#include <asm/arch/sprd_reg.h>
#include <asm/arch/common.h>
#include <asm/errno.h>

/*
#include <regs_adi.h>
#include "adi_hal_internal.h"
#include <asm/arch/sprd_reg.h>
#include <power/sprd_pmic/pmic_glb_reg.h>
#include "sprd_chg_helper.h"


#include <linux/bitops.h>
#include <linux/gpio/driver.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/i2c.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/of_gpio.h>
*/
#define HRTIMER_FRAME	20
#define AW9523B_DRIVER_VERSION		"driver version: 1.0.0"

#define AW9523_ID 0x23


#define AW9523_PORT_MAX 	(0x10) // 16
#define AW9523_KEY_PORT_MAX (0x10) // 16
#define AW9523_LED_MAX_NUMS (0x10) // 16

#define AWINIC_DEBUG		1
#if (defined (AWINIC_DEBUG)  &&  (AWINIC_DEBUG != 0))
#define AW_DEBUG(fmt, args...) \
	do {	\
		printk(fmt, ##args); \
	} while (0)
#else
#define AW_DEBUG(fmt, ...) \
	do {	\
	} while (0)
#endif

struct aw9523 {

	unsigned int npins;

	unsigned int input_port_mask;
	unsigned int int_port_mask;

	unsigned int old_input_state;
	unsigned int new_input_state;

	int irq_gpio;
	int irq_num;
	int rst_gpio;
};

enum aw9523_gpio_dir {
	AW9523_GPIO_INPUT = 0,
	AW9523_GPIO_OUTPUT = 1,
};

enum aw9523_gpio_val {
	AW9523_GPIO_HIGH = 1,
	AW9523_GPIO_LOW = 0,
};

enum aw9523_gpio_output_mode {
	AW9523_OPEN_DRAIN_OUTPUT = 0,
	AW9523_PUSH_PULL_OUTPUT = 1,
};

/*register list */
#define P0_INPUT		0x00
#define P1_INPUT 		0x01
#define P0_OUTPUT 		0x02
#define P1_OUTPUT 		0x03
#define P0_CONFIG		0x04
#define P1_CONFIG 		0x05
#define P0_INT			0x06
#define P1_INT			0x07
#define ID_REG			0x10
#define CTL_REG			0x11
#define P0_LED_MODE		0x12
#define P1_LED_MODE		0x13
#define P1_0_DIM0		0x20
#define P1_1_DIM0		0x21
#define P1_2_DIM0		0x22
#define P1_3_DIM0		0x23
#define P0_0_DIM0		0x24
#define P0_1_DIM0		0x25
#define P0_2_DIM0		0x26
#define P0_3_DIM0		0x27
#define P0_4_DIM0		0x28
#define P0_5_DIM0		0x29
#define P0_6_DIM0		0x2A
#define P0_7_DIM0		0x2B
#define P1_4_DIM0		0x2C
#define P1_5_DIM0		0x2D
#define P1_6_DIM0		0x2E
#define P1_7_DIM0		0x2F
#define SW_RSTN			0x7F

/*
struct pinctrl_pin_desc	aw9523_pin[] = {
	{.number = 0x00, .name = "P0_0"},
	{.number = 0x01, .name = "P0_1"},
	{.number = 0x02, .name = "P0_2"},
	{.number = 0x03, .name = "P0_3"},
	{.number = 0x04, .name = "P0_4"},
	{.number = 0x05, .name = "P0_5"},
	{.number = 0x06, .name = "P0_6"},
	{.number = 0x07, .name = "P0_7"},
	{.number = 0x08, .name = "P1_0"},
	{.number = 0x09, .name = "P1_1"},
	{.number = 0x0a, .name = "P1_2"},
	{.number = 0x0b, .name = "P1_3"},
	{.number = 0x0c, .name = "P1_4"},
	{.number = 0x0d, .name = "P1_5"},
	{.number = 0x0e, .name = "P1_6"},
	{.number = 0x0f, .name = "P1_7"},
};
*/

#define AW9523_GPIO_CHIP_MAX 4
static const char *aw9523_gpio_label_name[AW9523_GPIO_CHIP_MAX] = {
	"aw9523_gpio_a", "aw9523_gpio_b", "aw9523_gpio_c",
	"aw9523_gpio_d",
};

#define I2C_SPEED			(100000)
#define AW9523_SLAVE_ADDR 		(0x58)


unsigned int aw9523_gpio_init_flag = 0;

int aw9523_gpio_init(void)
{
	unsigned char val;
	unsigned char cnt;
	unsigned char reg_addr;

	debugf("%s \n", __func__);
	i2c_set_bus_num(CONFIG_AW9523_I2C_BUS);
	i2c_init(I2C_SPEED, AW9523_SLAVE_ADDR+1);
/*
	i2c_reg_write(AW9523_SLAVE_ADDR,P0_LED_MODE,0xFF);	// gpio mode
	i2c_reg_write(AW9523_SLAVE_ADDR,P1_LED_MODE,0xFF);	// gpio mode
	i2c_reg_write(AW9523_SLAVE_ADDR,P0_CONFIG,0x00);	// output mode
	i2c_reg_write(AW9523_SLAVE_ADDR,P1_CONFIG,0x00);	// output mode
	i2c_reg_write(AW9523_SLAVE_ADDR,CTL_REG,0x11);		// P0 push pull mode
*/
	i2c_reg_write(AW9523_SLAVE_ADDR+1,P0_LED_MODE,0xFF);
	i2c_reg_write(AW9523_SLAVE_ADDR+1,P1_LED_MODE,0xFF);
	i2c_reg_write(AW9523_SLAVE_ADDR+1,P0_CONFIG,0x00);	// output mode
	i2c_reg_write(AW9523_SLAVE_ADDR+1,P1_CONFIG,0x00);	// output mode
	i2c_reg_write(AW9523_SLAVE_ADDR+1,CTL_REG,0x11);		// push pull mode
	i2c_reg_write(AW9523_SLAVE_ADDR+1,P0_OUTPUT,0x00);	// output low
	i2c_reg_write(AW9523_SLAVE_ADDR+1,P1_OUTPUT,0x00);	// output low

	i2c_reg_write(AW9523_SLAVE_ADDR+2,P0_LED_MODE,0xFF);
	i2c_reg_write(AW9523_SLAVE_ADDR+2,P1_LED_MODE,0xFF);
	i2c_reg_write(AW9523_SLAVE_ADDR+2,P0_CONFIG,0x00);	// output mode
	i2c_reg_write(AW9523_SLAVE_ADDR+2,P1_CONFIG,0x00);	// output mode
	i2c_reg_write(AW9523_SLAVE_ADDR+2,CTL_REG,0x11);		// P0 push pull mode
	i2c_reg_write(AW9523_SLAVE_ADDR+2,P0_OUTPUT,0x00);	// output low
	i2c_reg_write(AW9523_SLAVE_ADDR+2,P1_OUTPUT,0x00);	// output low

	i2c_reg_write(AW9523_SLAVE_ADDR+3,P0_LED_MODE,0xFF);
	i2c_reg_write(AW9523_SLAVE_ADDR+3,P1_LED_MODE,0xFF);
	i2c_reg_write(AW9523_SLAVE_ADDR+3,P0_CONFIG,0x00);	// output mode
	i2c_reg_write(AW9523_SLAVE_ADDR+3,P1_CONFIG,0x00);	// output mode
	i2c_reg_write(AW9523_SLAVE_ADDR+3,CTL_REG,0x11);		// P0 push pull mode
	i2c_reg_write(AW9523_SLAVE_ADDR+3,P0_OUTPUT,0x00);	// output low
	i2c_reg_write(AW9523_SLAVE_ADDR+3,P1_OUTPUT,0x00);	// output low

	return 0;
}

unsigned char aw9523_offset_to_slave_addr(unsigned int offset)
{
	unsigned char slave_addr = 0;
	if(aw9523_gpio_init_flag == 0)
	{
		aw9523_gpio_init();
		aw9523_gpio_init_flag = 1;
	}

	if(offset >= 4*16){
		return 0;
	}else if((offset >= 3*16) && (offset < 4*16)){
		slave_addr = 0x5b;
	}else if((offset >= 2*16) && (offset < 3*16)){
		slave_addr = 0x5a;
	}else if((offset >= 16) && (offset < 2*16)){
		slave_addr = 0x59;
	}else if((offset >= 0) && (offset < 16)){
		//slave_addr = 0x58;
		return 0;
	}

	//debugf("%s offset=%d, slave_addr=0x%x \n", __func__, offset, slave_addr);
	return slave_addr;

}

static unsigned int i2c_write_byte(unsigned char slave_addr, unsigned char reg_addr, unsigned char reg_data)
{

	debugf("aw9523_write  slave_addr=%x reg_addr=0x%x reg_data=%d/0x%x\n", slave_addr,reg_addr, reg_data,reg_data);
	i2c_reg_write(slave_addr,reg_addr,reg_data);
   	return 0;
}
static unsigned int i2c_read_byte(unsigned char slave_addr, unsigned char reg_addr, unsigned char *val)
{
	int ret = 0;

	ret = i2c_reg_read(slave_addr , reg_addr);
	if (ret < 0) {
		debugf("%s fail reg_addr(0x%x), ret(%d)\n", __func__, reg_addr, ret);
		return ret;
	}

	ret &= 0xff;
	*val = ret;
	debugf("aw9523_read slave_addr=%x reg_addr=0x%x value=%d/0x%x\n", slave_addr, reg_addr, ret, ret);
	return ret;
}

/*
static unsigned int i2c_read_multi_byte(unsigned char slave_addr, unsigned char reg_addr, unsigned char *buf, unsigned int len)
{
	int ret = 0;

	return ret;
}
*/

/******************************************************
 *
 * aw9523 chip ctrol interface
 *
 ******************************************************/
/* val 1-> gpio mode  0->led mode */
static void aw9523_set_gpio_mode_by_mask(unsigned char slave_addr, unsigned int mask, int val)
{
	unsigned char reg_val[2] = {0};
	int i = 0;
	i2c_read_byte(slave_addr, P0_LED_MODE, &reg_val[0]); //p0_0 -- p0_7
	i2c_read_byte(slave_addr, P1_LED_MODE, &reg_val[1]); //p1_0 -- p1_7
	/* 0-output 1-input */
	for (i = 0; i < AW9523_KEY_PORT_MAX; i++) {
		if (mask & (0x01 << i)) {
			if (val) {
				if (i < 8) {
					reg_val[0] |=  0x1 << i;
				} else {
					reg_val[1] |=  0x1 << (i - 8);
				}
			} else {
				if (i < 8) {
					reg_val[0] &=  ~(0x1 << i);
				} else {
					reg_val[1] &=  ~(0x1 << (i - 8));
				}
			}
		}
	}
	i2c_write_byte(slave_addr, P0_LED_MODE, reg_val[0]);
	i2c_write_byte(slave_addr, P1_LED_MODE, reg_val[1]);

	i2c_read_byte(slave_addr, P0_LED_MODE, &reg_val[0]); //p0_0 -- p0_7
	i2c_read_byte(slave_addr, P1_LED_MODE, &reg_val[1]); //p1_0 -- p1_7
}

/* val 1-> output  0->input */
static void aw9523_set_port_direction_by_mask(unsigned char slave_addr, unsigned int mask, int val)
{
	unsigned char reg_val[2] = {0};
	int i = 0;
	i2c_read_byte(slave_addr, P0_CONFIG, &reg_val[0]); //p0_0 -- p0_7
	i2c_read_byte(slave_addr, P1_CONFIG, &reg_val[1]); //p1_0 -- p1_7
	/* 0-output 1-input */
	for (i = 0; i < AW9523_KEY_PORT_MAX; i++) {
		if (mask & (0x01 << i)) {
			if (!val) {
				if (i < 8) {
					reg_val[0] |=  0x1 << i;
				} else {
					reg_val[1] |=  0x1 << (i - 8);
				}
			} else {
				if (i < 8) {
					reg_val[0] &=  ~(0x1 << i);
				} else {
					reg_val[1] &=  ~(0x1 << (i - 8));
				}
			}
		}
	}
	i2c_write_byte(slave_addr, P0_CONFIG, reg_val[0]);
	i2c_write_byte(slave_addr, P1_CONFIG, reg_val[1]);
}

/* return  0=out, 1=in */
static int aw9523_get_port_direction_by_mask(unsigned char slave_addr, unsigned int mask)
{
	unsigned char reg_val[2] = {0};
	unsigned int dir_reg = 0;
	//i2c_read_multi_byte(slave_addr, P0_CONFIG, reg_val, sizeof(reg_val) / sizeof(reg_val[0]));
	i2c_read_byte(slave_addr, P0_CONFIG, &reg_val[0]); //p0_0 -- p0_7
	i2c_read_byte(slave_addr, P1_CONFIG, &reg_val[1]); //p1_0 -- p1_7
	dir_reg = (reg_val[1] << 8) | reg_val[0];
	return (dir_reg & mask) ? 1 : 0;
}

/* val 1-> output high  0->output low */
static void aw9523_set_port_output_state_by_mask(unsigned char slave_addr, unsigned int mask, int val)
{
	unsigned char reg_val[2] = {0};
	int i = 0;
	i2c_read_byte(slave_addr, P0_OUTPUT, &reg_val[0]); //p0_0 -- p0_7
	i2c_read_byte(slave_addr, P1_OUTPUT, &reg_val[1]); //p1_0 -- p1_7
	for (i = 0; i < AW9523_KEY_PORT_MAX; i++) {
		if (mask & (0x01 << i)) {
			if (val) {
				if (i < 8) {
					reg_val[0] |=  0x1 << i;
				} else {
					reg_val[1] |=  0x1 << (i - 8);
				}
			} else {
				if (i < 8) {
					reg_val[0] &=  ~(0x1 << i);
				} else {
					reg_val[1] &=  ~(0x1 << (i - 8));
				}
			}
		}
	}
	i2c_write_byte(slave_addr, P0_OUTPUT, reg_val[0]);
	i2c_write_byte(slave_addr, P1_OUTPUT, reg_val[1]);
}

/* val 1-> input high  0->input low */
static unsigned int aw9523_get_port_input_state(unsigned char slave_addr)
{
	unsigned char reg_val[2] = {0};
	//i2c_read_multi_byte(slave_addr, P0_INPUT, &reg_val[0], sizeof(reg_val) / sizeof(reg_val[0]));
	i2c_read_byte(slave_addr, P0_INPUT, &reg_val[0]); //p0_0 -- p0_7
	i2c_read_byte(slave_addr, P1_INPUT, &reg_val[1]); //p1_0 -- p1_7
	return reg_val[0] | (reg_val[1] << 8);
}

/* val 1-> input high  0->input low */
static unsigned int aw9523_get_port_output_state(unsigned char slave_addr)
{
	unsigned char reg_val[2] = {0};
	//i2c_read_multi_byte(slave_addr, P0_OUTPUT, &reg_val[0], sizeof(reg_val) / sizeof(reg_val[0]));
	i2c_read_byte(slave_addr, P0_OUTPUT, &reg_val[0]); //p0_0 -- p0_7
	i2c_read_byte(slave_addr, P1_OUTPUT, &reg_val[1]); //p1_0 -- p1_7
	return reg_val[0] | (reg_val[1] << 8);
}

/*
static int aw9523_reset(struct aw9523 *p_aw9523)
{
	int ret = 0;
	struct device_node *node = p_aw9523->dev->of_node;
	p_aw9523->rst_gpio = of_get_named_gpio(node, "reset-gpio", 0);
	if ((!gpio_is_valid(p_aw9523->rst_gpio))){
		dev_err(p_aw9523->dev, "%s: dts don't provide reset-gpio\n",__func__);
		return -EINVAL;
	}

	ret = gpio_request(p_aw9523->rst_gpio, "aw9523-reset");
	if (ret) {
		dev_err(&p_aw9523->client->dev, "%s: unable to request gpio [%d]\n",__func__, p_aw9523->rst_gpio);
		return ret;
	}

	ret = gpio_direction_output(p_aw9523->rst_gpio, 1);
	if(ret) {
		gpio_free(p_aw9523->rst_gpio);
		dev_err(p_aw9523->dev, "%s: unable to set direction of gpio\n",__func__);
		return ret;
	}
	gpio_set_value(p_aw9523->rst_gpio, 1);
	msleep(1);
	gpio_set_value(p_aw9523->rst_gpio, 0);
	msleep(1);
	gpio_set_value(p_aw9523->rst_gpio, 1);
	return ret;


}
*/



/*********************************************************
 *
 * aw9523 check chipid
 *
 ********************************************************/
int aw9523_read_chipid(unsigned char slave_addr)
{
	unsigned char val;
	unsigned char cnt;

	for (cnt = 5; cnt > 0; cnt--) {
		i2c_read_byte(slave_addr, ID_REG, &val);
		debugf("aw9523_read_chipid val=0x%x\n", val); //0x80:aw9523a,0x23:aw9523b
		if (val == AW9523_ID) {
			return 0;
		}
		msleep(5);
	}
	return -EINVAL;
}


// return	0=out, 1=in
int aw9523_gpio_get_direction( unsigned int offset)
{
	unsigned char slave_addr = 0;

	slave_addr = aw9523_offset_to_slave_addr(offset);
	if(slave_addr == 0)
		return -1;

	debugf("%s offset=%d slave_addr=0x%x \n", __func__, offset, slave_addr);

	return aw9523_get_port_direction_by_mask(slave_addr, (0x01 << (offset%16)));

}

int aw9523_gpio_direction_input( unsigned int offset)
{
	unsigned char slave_addr = 0;

	slave_addr = aw9523_offset_to_slave_addr(offset);
	if(slave_addr == 0)
		return -1;

	debugf("%s offset=%d slave_addr =0x%x \n", __func__, offset, slave_addr);
	//p_aw9523->input_port_mask |= 0x01 << offset;
	aw9523_set_port_direction_by_mask(slave_addr, (0x01 << (offset%16)), 0);
	return 0;

}

int aw9523_gpio_direction_output( unsigned int offset, int value)
{
	unsigned char slave_addr = 0;

	slave_addr = aw9523_offset_to_slave_addr(offset);
	if(slave_addr == 0)
		return -1;

	debugf("%s offset=%d value=%d, slave_addr=0x%x \n", __func__, offset, value, slave_addr);
	aw9523_set_port_direction_by_mask(slave_addr, (0x01 << (offset%16)), 1);
	aw9523_set_port_output_state_by_mask(slave_addr, (0x01 << (offset%16)), !(!value));
	return 0;

}

int aw9523_gpio_get( unsigned int offset)
{
	unsigned char slave_addr = 0;
	unsigned int ret = 0;
	int gpio_status = 0;

	slave_addr = aw9523_offset_to_slave_addr(offset);
	debugf("%s offset=%d slave_addr=0x%x \n", __func__, offset, slave_addr);
	if(slave_addr == 0)
		return -1;

	if (aw9523_gpio_get_direction( offset)) {
		ret = aw9523_get_port_input_state(slave_addr);
	} else {
		ret = aw9523_get_port_output_state(slave_addr);
	}
	gpio_status = !!(ret & (0x01<< (offset%16)));
	debugf("%s rawstatus=%x gpio_status=%d\n", __func__, ret, gpio_status);
	return gpio_status;
}

void aw9523_gpio_set( unsigned offset, int value)
{
	unsigned char slave_addr = 0;
	
	slave_addr = aw9523_offset_to_slave_addr(offset);
	debugf("%s offset=%d value=%d slave_addr=0x%x \n", __func__, offset, value, slave_addr);
	if(slave_addr == 0)
		return -1;

	aw9523_set_port_output_state_by_mask(slave_addr, (0x01 << (offset%16)), !(!value));
}



int aw9523_gpio_request( unsigned int offset)
{
	unsigned char slave_addr = 0;

	slave_addr = aw9523_offset_to_slave_addr(offset);
	debugf("%s offset=%d slave_addr=0x%x \n", __func__, offset, slave_addr);

	if(slave_addr == 0)
		return -1;
	aw9523_set_gpio_mode_by_mask(slave_addr, (0x01<<(offset%16)), 1);
	return 0;

}


void aw9523_gpio_free( unsigned int offset)
{
	unsigned char slave_addr = 0;

	slave_addr = aw9523_offset_to_slave_addr(offset);
	debugf("%s offset=%d slave_addr=0x%x \n", __func__, offset, slave_addr);
	if(slave_addr == 0)
		return -1;

//	aw9523_set_gpio_mode_by_mask(slave_addr, (0x01<<(offset%16)), 0);
	return;

}


